/*
 * Created on :Jun 16, 2012 Author :songlin.li
 */
package com.dmagic.cms.common.util.reflection;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.apache.commons.beanutils.converters.SqlTimestampConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dmagic.cms.common.util.ClassUtil;
import com.dmagic.cms.common.util.StringUtil;

public class BeanUtil extends BeanUtils {
    private static Logger logger = LoggerFactory.getLogger(ReflectionUtil.class);

    private BeanUtil() {
    }

    static {
        ConvertUtil.register(new DateConvert(), java.sql.Date.class);
        ConvertUtil.register(new DateConvert(), java.util.Date.class);
        // ConvertUtils.register(new SqlDateConverter(), java.util.Date.class);
        // ConvertUtils.register(new SqlTimestampConverter(),
        // java.sql.Timestamp.class);
        // ConvertUtils.register(new UtilDateConverter(), java.util.Date.class);
    }

    /**
     * override copy properties, convert with date
     * 
     * @author songlin.li
     * @param target
     * @param source
     */
    public static void copyProperties(Object target, Object source) {
        try {
            BeanUtils.copyProperties(target, source);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * override populate, convert with date
     * 
     * @author songlin.li
     * @param target
     * @param source
     */
    public static void populate(Object target, Map source) {
        try {
            BeanUtils.populate(target, source);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * copy source filed value to target, when source filed value is null then
     * not convert target value
     * 
     * @author songlin.li
     * @param target
     * @param source
     * @return target
     */
    public static Object wrap(Object target, Object source) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(source.getClass());
            PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
            for (int i = 0; i < properties.length; i++) {
                Method get = null;
                Method set = null;
                try {
                    get = properties[i].getReadMethod();
                    set = target.getClass().getMethod(properties[i].getWriteMethod().getName(),
                            new Class[] { properties[i].getPropertyType() });
                } catch (Throwable a) {
                    continue;
                }
                if (set != null && get != null) {
                    Object value = get.invoke(source);
                    if (value == null) {
                        continue;
                    }
                    set.invoke(target, value);
                }
            }
        } catch (IntrospectionException e) {
            logger.error(e.getMessage(), e);
        } catch (IllegalArgumentException e) {
            logger.error(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            logger.error(e.getMessage(), e);
        }
        return target;

    }

    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
        if (beanClass == null) {
            return null;
        }
        Object bean = null;
        try {
            bean = beanClass.newInstance();
        } catch (IllegalAccessException e) {
            logger.error("", e);
        } catch (InstantiationException e) {
            logger.error("", e);
        }
        mapToBean(map, bean);
        return (T) bean;
    }

    public static void mapToBean(Map<String, Object> map, Object bean) {
        if (bean == null || map == null) {
            return;
        }

        Map<String, Field> fieldMap = ClassUtil.getFields(bean);
        if (fieldMap.size() == 0) {
            return;
        }

        Map<String, String> fieldNameMap = new HashMap<String, String>();

        for (String key : fieldMap.keySet()) {
            Field field = fieldMap.get(key);
            field.setAccessible(true);
            String filedName = field.getName();
            fieldNameMap.put(filedName, filedName);
            fieldNameMap.put(filedName.toLowerCase(), filedName);
        }

        for (String key : map.keySet()) {
            Object value = map.get(key);

            String filedName = fieldNameMap.get(key.toLowerCase());
            if (filedName == null) {
                key = key.replace("_", "");
                filedName = fieldNameMap.get(key.toLowerCase());
            }

            if (filedName != null) {
                try {
                    ClassUtil.setFieldValue(bean, filedName, value);
                } catch (Exception e) {
                    logger.error("", e);
                }
            }
        }
    }

    public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass, String prefix) {
        if (beanClass == null) {
            return null;
        }
        Object bean = null;
        try {
            bean = beanClass.newInstance();
        } catch (IllegalAccessException e) {
            logger.error("", e);
        } catch (InstantiationException e) {
            logger.error("", e);
        }
        mapToBean(map, bean, prefix);
        return (T) bean;
    }

    public static void mapToBean(Map<String, Object> map, Object bean, String prefix) {
        if (bean == null || map == null) {
            return;
        }
        if (StringUtil.isBlank(prefix)) {
            String entityName = bean.getClass().getName().toLowerCase();
            String[] entityNames = entityName.split("\\.");
            entityName = entityNames[entityNames.length - 1];
            prefix = entityName + ".";
        } else if (!prefix.endsWith(".")) {
            prefix += ".";
        }
        Map<String, Field> fieldMap = ClassUtil.getFields(bean);
        if (fieldMap.size() == 0) {
            return;
        }

        Map<String, String> fieldNameMap = new HashMap<String, String>();

        for (String key : fieldMap.keySet()) {
            Field field = fieldMap.get(key);
            field.setAccessible(true);
            String filedName = field.getName();
            fieldNameMap.put(filedName, filedName);
            fieldNameMap.put(filedName.toLowerCase(), filedName);
        }

        for (String key : map.keySet()) {
            Object value = map.get(key);
            if (StringUtil.isNotBlank(prefix) && key.startsWith(prefix)) {
                key = StringUtil.substringAfter(key, prefix);
            }
            String filedName = fieldNameMap.get(key.toLowerCase());
            if (filedName == null) {
                key = key.replace("_", "");
                filedName = fieldNameMap.get(key.toLowerCase());
            }

            if (filedName != null) {
                try {
                    ClassUtil.setFieldValue(bean, filedName, value);
                } catch (Exception e) {
                    logger.error("", e);
                }
            }
        }
    }

    /**
     * bean property is key and value is value
     * 
     * @param bean
     * @author songlin.li
     */
    public static Map<String, Object> beanToMap(Object bean) {
        if (bean == null) {
            return null;
        }

        Map<String, Field> fieldMap = ClassUtil.getFields(bean);
        if (fieldMap.size() == 0) {
            return null;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        for (String key : fieldMap.keySet()) {
            map.put(key, ClassUtil.getFieldValue(bean, key));
        }
        return map;
    }
}
